package ncrnadb.ncinetview.internal.owrapper;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.orientechnologies.orient.core.index.OCompositeKey;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Index;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientBaseGraph;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;

public class ORDB {

//	public String CONNECTION_URL = "remote:localhost";
//	public String CONNECTION_USER = "admin";
//	public String CONNECTION_PASSW = "admin";
//	public String DB_NAME = "ncrnadb";
	
	//public String CONNECTION_URL = "remote:157.27.10.91";
	public String CONNECTION_URL = "remote:ncrnadb.scienze.univr.it";
	public String CONNECTION_USER = "reader";
	public String CONNECTION_PASSW = "reader";
	public String DB_NAME = "ncrnadb.1.0";
	
	
	public ORDB(){
	}
	
	public ORDB(String url, String dbname, String user, String pass){
		this.CONNECTION_URL = url;
		this.DB_NAME = dbname;
		this.CONNECTION_USER = user;
		this.CONNECTION_PASSW = pass;
	}
	
	public OrientBaseGraph graph = null;
	
	public void open(){
		graph = new OrientGraph(CONNECTION_URL + "/"+DB_NAME, CONNECTION_USER, CONNECTION_PASSW);
		getOSourceTypes();
		getOBioEntityClusters();
	}
	public void close(){
		graph.shutdown();
	}
	
	
	
	public Source getOSource(String s){
		return oRSourceTypes.get(s);
	}
	public String getOSource(Source s){
		return oSourceTypes.get(s);
	}
	public EntityType getOEntityType(Integer i){
		return oRBioEntityClusters.get(i);
	}
	public Integer getOEntityType(EntityType etype){
		return oBioEntityClusters.get(etype);
	}
	
	
	
	public Map<Source, String> oSourceTypes = null;
	public Map<String, Source> oRSourceTypes = null;
	
	private void getOSourceTypes(){
		oSourceTypes = new TreeMap<Source, String>();
		oRSourceTypes = new TreeMap<String, Source>();
		Iterable<Vertex> vs = graph.getVerticesOfClass("Source");
		for(Vertex v : vs){
			Source source = Source.get(v.getProperty("name").toString(), v.getProperty("version").toString());
			oSourceTypes.put(source, v.getId().toString());
			oRSourceTypes.put(v.getId().toString(), source);
		}
	}
	
	public Map<EntityType, Integer> oBioEntityClusters = null;
	public Map<Integer, EntityType> oRBioEntityClusters = null;
	
	private void getOBioEntityClusters(){
		oBioEntityClusters = new TreeMap<EntityType, Integer>();
		oRBioEntityClusters = new TreeMap<Integer, EntityType>();
		for(EntityType etype : EntityType.values()){
//			System.out.println(etype.type + " "+ graph.getRawGraph().getClusterIdByName("BioEntity."+etype.type));
			oBioEntityClusters.put(etype, graph.getRawGraph().getClusterIdByName("BioEntity."+etype.type));
			oRBioEntityClusters.put(graph.getRawGraph().getClusterIdByName("BioEntity."+etype.type), etype);
		}
		
		for(EntityType etype : EntityType.values()){
//			System.out.println("Alias."+etype.type + " "+ graph.getRawGraph().getClusterIdByName("Alias."+etype.type));
			oRBioEntityClusters.put(graph.getRawGraph().getClusterIdByName("Alias."+etype.type), etype);
		}
	}
	
	
	public static String getIndexName(EntityType etype, Source source){
		if(etype == null)
			return "Alias.name";
		if(source == null){
			return "Alias." + etype.type + ".uname";
		}
		return "Alias." + etype.type + ".name";
		
	}
	
	
	public Iterable<Vertex> getAliases(Vertex entity){
		return entity.getVertices(Direction.IN, "AliasToEntity");
	}
	
	public Collection<Vertex> getAliases(Collection<Vertex> entities){
		Collection<Vertex> aliases = new TreeSet<Vertex>();
		for(Vertex v : entities){
			for(Vertex a : getAliases(v))
				aliases.add(a);
		}
		return aliases;
	}
	
	
	
	public RID getRID(Vertex alias){
		return new RID(
				oRBioEntityClusters.get( (((OrientVertex)alias).getIdentity().getClusterId()) ),
				oRSourceTypes.get( ((OrientVertex)alias.getProperty("source")).getId().toString() ),
				alias.getProperty("name").toString()
				);
	}
	
	public Collection<RID> getRIDs(Vertex entity){
		Collection<RID> rids = new TreeSet<RID>();
		Iterable<Vertex> aliases = getAliases(entity);
		for(Vertex v : aliases){
			rids.add(getRID(v));
		}
		return rids;
	}
	public Collection<RID> getRIDs(Collection<Vertex> entities){
		Collection<RID> rids = new TreeSet<RID>();
		for(Vertex entity :  entities){
			Iterable<Vertex> aliases = getAliases(entity);
			for(Vertex v : aliases){
				rids.add(getRID(v));
			}
		}
		return rids;
	}
	
	
	
	
	
	
	public Iterable<Vertex> getAliasVertices(RID rid){
		//Collection<OrientVertex> verts = new TreeSet<OrientVertex>();
		if(rid.etype == null){
			//return graph.getVertices( getIndexName(null, null) , rid.id);
			
			//System.out.println("select from index:"+getIndexName(null,null)+" where key=\""+rid.id +"\"");
			
			Collection<Vertex> res = new LinkedList<Vertex>();
			List<ODocument> result = graph.getRawGraph().query(
					  new OSQLSynchQuery<ODocument>("select from index:"+getIndexName(null,null)+" where key=\""+rid.id +"\""));
			for(ODocument doc : result){
				res.add(graph.getVertex(doc.field("rid", String.class)));
			}
			return res;
			
		}
		else if(rid.source == null){
			//return graph.getVertices( getIndexName(rid.etype, null) , rid.id);
			
			//System.out.println("select from index:"+getIndexName(rid.etype, null)+" where key=\""+rid.id +"\"");
			
			Collection<Vertex> res = new LinkedList<Vertex>();
			List<ODocument> result = graph.getRawGraph().query(
					  new OSQLSynchQuery<ODocument>("select from index:"+getIndexName(rid.etype, null)+" where key=\""+rid.id +"\""));
			for(ODocument doc : result){
				res.add(graph.getVertex(doc.field("rid", String.class)));
			}
			return res;
		}
		else{
			//System.out.println("3 level index "+getIndexName(rid.etype, rid.source)+" |"+ rid.id+"|"+ oSourceTypes.get(rid.source)+"|");
			//return graph.getVertices( getIndexName(rid.etype, rid.source) , new OCompositeKey(rid.id, oSourceTypes.get(rid.source)));
			
			//System.out.println("select from index:"+getIndexName(rid.etype, rid.source)+" where key=[\""+rid.id +"\","+ oSourceTypes.get(rid.source) +"]");
			
			
			Collection<Vertex> res = new LinkedList<Vertex>();
			List<ODocument> result = graph.getRawGraph().query(
					  new OSQLSynchQuery<ODocument>("select from index:"+getIndexName(rid.etype, rid.source)+" where key=[\""+rid.id +"\","+ oSourceTypes.get(rid.source) +"]"));
			
			for(ODocument doc : result){
				//System.out.println(doc.field("rid", String.class));
				res.add(graph.getVertex(doc.field("rid", String.class)));
			}
			return res;
			
		}	
		//return verts;
	}
	
	
	
	public Iterable<Vertex> getEntitiesOfType(EntityType etype){
		//return graph.getVerticesOfClass("cluster:BioEntity."+ etype.type);
		Collection<Vertex> res = new LinkedList<Vertex>();
		List<ODocument> result = graph.getRawGraph().query(
				  new OSQLSynchQuery<ODocument>("select from cluster:BioEntity."+etype.type));
		
		for(ODocument doc : result){
			//System.out.println(doc.field("rid", String.class));
			res.add(graph.getVertex(doc.field("@RID", String.class)));
		}
		return res;
	}
	
	
	public Collection<Vertex> getEntities(RID rid){
		Collection<Vertex> verts = new TreeSet<Vertex>();
		
		if(rid.isORID){
			verts.add(graph.getVertex(rid.id));
		}
		else{
			Iterable<Vertex> averts = getAliasVertices(rid);
			for(Vertex a : averts){
				Iterable<Vertex> vv = a.getVertices(Direction.OUT, "AliasToEntity");
				for(Vertex v : vv){
					verts.add(v);
				}
			}
		}
		
		return verts;
	}
	
	
	
	public Collection<Vertex> getEntities(Collection<RID> rids){
		Collection<Vertex> verts = new TreeSet<Vertex>();
		
		for(RID rid : rids){
			if(rid.isORID){
				verts.add(graph.getVertex(rid.id));
			}
			else{
				Iterable<Vertex> averts = getAliasVertices(rid);
				for(Vertex a : averts){
					Iterable<Vertex> vv = a.getVertices(Direction.OUT, "AliasToEntity");
					for(Vertex v : vv){
						verts.add(v);
					}
				}
			}	
		}
		
		return verts;
	}
	
	
	public Collection<Vertex> getEntityNeighbors(Vertex v){
		Collection<Vertex> verts = new TreeSet<Vertex>();
		
		Direction[] dirs = {Direction.IN, Direction.OUT}; 
		
		for(Direction dir : dirs){
			//Direction rdir = Direction.IN;
			//if(dir == Direction.IN)
			//	rdir = Direction.OUT;
		
			Iterable<Vertex> nn = v.getVertices(dir, "EInteraction");
			for(Vertex n : nn){
				verts.add(n);
			}
		}
		
		return verts;
	}
	
	
	public Collection<Edge> getEntityRelations(Vertex v){
		Collection<Edge> edges = new TreeSet<Edge>();
		
		Direction[] dirs = {Direction.IN, Direction.OUT}; 
		
		for(Direction dir : dirs){			
		
			Iterable<Edge> eds = v.getEdges(dir, "EInteraction");
			for(Edge ed : eds){
				edges.add(ed);
			}
		}
		
		return edges;
	}
	
	
	public Collection<Edge> getRelationsAmong(Collection<Vertex> verts){
		Collection<Edge> edges = new TreeSet<Edge>();
		for(Vertex v : verts){
			Iterable<Edge> eds = v.getEdges(Direction.OUT, "EInteraction");
			for(Edge ed : eds){
				if(verts.contains(ed.getVertex(Direction.IN)))
					edges.add(ed);
			}
		}
		return edges;
	}
	
	public RelationFields getFields(Edge ed){
		RelationFields rf = new RelationFields();
		
		Vertex fields = graph.getVertex(ed.getProperty("fields"));
		rf.level = fields.getProperty("level");
		rf.pubmedID = fields.getProperty("pubmed");
		rf.supportSentence = fields.getProperty("support");
		rf.source =  oRSourceTypes.get( ((OrientVertex)fields.getProperty("source")).getId().toString() );						
		
		return rf;
	}
	
	
	
	
	
	public Collection<Vertex> getEntityNeighbors(Vertex v, Collection<EntityType> ntypes){
		Set<Integer> etypes = new TreeSet<Integer>();
		for(EntityType e : ntypes){
			etypes.add(getOEntityType(e));
		}
		return getEntityNeighborsByClusters(v, etypes);
	}
	
	public Collection<Vertex> getEntityNeighborsByClusters(Vertex v, Collection<Integer> ntypes){
		Collection<Vertex> verts = new TreeSet<Vertex>();
		
		Direction[] dirs = {Direction.IN, Direction.OUT}; 
		
		for(Direction dir : dirs){
			Iterable<Vertex> nn = v.getVertices(dir, "EInteraction");
			for(Vertex n : nn){
				if(ntypes.contains(  (((OrientVertex)n).getIdentity().getClusterId())  )){
					verts.add(n);
				}
			}
		}
		
		return verts;
	}
	
}
